package cn.wolfcode.shop.cloud.service.impl;

import cn.wolfcode.shop.cloud.common.BusinessException;
import cn.wolfcode.shop.cloud.common.JSONUtil;
import cn.wolfcode.shop.cloud.common.Result;
import cn.wolfcode.shop.cloud.domain.Good;
import cn.wolfcode.shop.cloud.domain.SeckillGood;
import cn.wolfcode.shop.cloud.feign.GoodsFeignApi;
import cn.wolfcode.shop.cloud.mapper.SeckillGoodMapper;
import cn.wolfcode.shop.cloud.redis.key.SeckillRedisKey;
import cn.wolfcode.shop.cloud.service.ISeckillGoodService;
import cn.wolfcode.shop.cloud.vo.SeckillGoodVo;
import cn.wolfcode.shop.cloud.web.msg.SeckillServerCodeMsg;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * Created by wolfcode-lanxw
 */
@Service
public class SeckillGoodServiceImpl implements ISeckillGoodService {

    @Autowired
    private SeckillGoodMapper seckillGoodMapper;
    @Autowired
    private GoodsFeignApi goodsFeignApi;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public List<SeckillGoodVo> listAll() {
        // 1. 查詢所有秒杀商品列表
        List<SeckillGood> list = seckillGoodMapper.selectAll();
        return seckillGoodsDetail(list);
    }

    @Override
    public List<SeckillGoodVo> queryByCache() {
        // 从redis中取出所有缓存的秒杀商品信息
        List<Object> values = redisTemplate.opsForHash().values(SeckillRedisKey.SECKILL_GOODS_HASH.join());
        if (!CollectionUtils.isEmpty(values)) {
            List<SeckillGoodVo> ret = new ArrayList<>(values.size());
            // 遍历解析json对象, 并取到最新的库存数据
            for (Object value : values) {
                String json = (String) value;
                if (!StringUtils.isEmpty(json)) {
                    SeckillGoodVo vo = JSONUtil.parseObject(json, SeckillGoodVo.class);
                    if (vo != null) {
                        // 由于库存数量是在redis进行预减的, 所以最新的库存数量要从 redis 中读取
                        String stockCountStr = (String) redisTemplate.opsForHash().get(SeckillRedisKey.SECKILL_STOCK_COUNT_HASH.join(), vo.getId() + "");
                        if (!StringUtils.isEmpty(stockCountStr)) {
                            int stockCount = Integer.parseInt(stockCountStr);
                            vo.setStockCount(stockCount <= 0 ? 0 : stockCount);
                        }
                    }
                    ret.add(vo);
                }
            }
            return ret;
        }
        return null;
    }

    @Override
    public SeckillGoodVo findById(Long id) {
        SeckillGood seckillGood = seckillGoodMapper.selectByPrimaryKey(id);
        List<SeckillGoodVo> voList = seckillGoodsDetail(Collections.singletonList(seckillGood));
        return CollectionUtils.isEmpty(voList) ? null : voList.get(0);
    }

    @Override
    public SeckillGoodVo findByCache(Long id) {
        String json = (String) redisTemplate.opsForHash().get(SeckillRedisKey.SECKILL_GOODS_HASH.join(), id + "");
        if (!StringUtils.isEmpty(json)) {
            SeckillGoodVo vo = JSONUtil.parseObject(json, SeckillGoodVo.class);
            // 由于库存数量是在redis进行预减的, 所以最新的库存数量要从 redis 中读取
            String stockCountStr = (String) redisTemplate.opsForHash().get(SeckillRedisKey.SECKILL_STOCK_COUNT_HASH.join(), id + "");
            if (vo != null && !StringUtils.isEmpty(stockCountStr)) {
                int stockCount = Integer.parseInt(stockCountStr);
                vo.setStockCount(stockCount <= 0 ? 0 : stockCount);
            }
            return vo;
        }
        return null;
    }

    @Override
    public int decrStockCount(Long seckillId) {
        return seckillGoodMapper.decrStockCount(seckillId);
    }

    @Override
    public void incrStockCount(Long seckillId) {
        seckillGoodMapper.incrStockCount(seckillId);
    }

    private List<SeckillGoodVo> seckillGoodsDetail(List<SeckillGood> list) {
        // 2. 遍历秒杀商品列表, 得到一个商品id列表
        Set<Long> idList = new HashSet<>(list.size());
        for (SeckillGood seckillGood : list) {
            idList.add(seckillGood.getGoodId());
        }
        // 函数式编程: 把一个函数作为参数传递给另一个函数进行调用
        //Set<Long> idList = list.stream().map(SeckillGood::getGoodId).collect(Collectors.toSet());

        // 3. 调用远程商品服务, 以商品id列表作为参数查询商品列表
        // 根据商品id列表调用商品服务查询商品列表
        Result<List<Good>> result = goodsFeignApi.findByIdList(idList);
        /**
         * result 可能有几种响应
         * 1. 正常执行业务并返回, code = 200, data = list
         * 2. 如果执行业务报错, code != 200
         * 3. 如果进入降级方法, result = null
         */
        if (result == null || result.hasError()) {
            // 异常情况处理, 根据业务场景做不同的处理
            throw new BusinessException(SeckillServerCodeMsg.DEFALUT_ERROR);
        }
        List<Good> goodList = result.getData();
        // 4. 遍历商品列表,构建map,以商品id为key,商品对象为value
        Map<Long, Good> tmpCache = new HashMap<>(goodList.size());
        for (Good good : goodList) {
            tmpCache.put(good.getId(), good);
        }
        // 5. 遍历秒杀商品列表, 根据秒杀商品的商品id,从map中查询到对应的商品对象
        List<SeckillGoodVo> voList = new ArrayList<>(list.size());
        for (SeckillGood seckillGood : list) {
            SeckillGoodVo vo = new SeckillGoodVo();
            voList.add(vo);
            // 6. 使用 BeanUtils.copyProperties 拷贝属性到 vo 对象
            Good good = tmpCache.get(seckillGood.getGoodId());
            if (good != null) {
                BeanUtils.copyProperties(good, vo);
            }
            // 将秒杀商品属性copy到 vo对象中
            BeanUtils.copyProperties(seckillGood, vo);
        }

        return voList;
    }

//    @Override
//    public SeckillGoodVo findById(Long id) {
//        // 1. 根据 id 查询出秒杀商品对象
//        SeckillGood sg = seckillGoodMapper.selectByPrimaryKey(id);
//        // 2. 根据秒杀商品对象的商品id调用远程服务查询出商品对象
//        Result<List<Good>> result = goodsFeignApi.findByIdList(Collections.singleton(sg.getGoodId()));
//        if (result == null || result.hasError()) {
//            return null;
//        }
//        // 3. 创建 vo 对象,并将秒杀商品对象和商品对象的属性拷贝到 vo 对象中
//        SeckillGoodVo vo = new SeckillGoodVo();
//        List<Good> goods = result.getData();
//        if (!CollectionUtils.isEmpty(goods)) {
//            BeanUtils.copyProperties(goods.get(0), vo);
//        }
//        BeanUtils.copyProperties(sg, vo);
//        // 4. 返回 vo 对象
//        return vo;
//    }
}
